home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 3: Developer Tools / Linux Cubed Series 3 - Developer Tools.iso / devel / lang / lisp / clisp-li.000 / clisp-li / clisp-1996-07-22 / src / conditio.lsp < prev    next >
Encoding:
Lisp/Scheme  |  1996-06-05  |  62.7 KB  |  1,627 lines

  1. ;;; Condition System for CLISP
  2. ;;; David Gadbois <gadbois@cs.utexas.edu> 30.11.1993
  3. ;;; Bruno Haible 24.11.1993, 2.12.1993
  4.  
  5. (in-package "LISP")
  6. ;;; exports:
  7. (export '(
  8. ;; types:
  9. restart condition serious-condition error program-error control-error
  10. arithmetic-error division-by-zero floating-point-overflow
  11. floating-point-underflow cell-error unbound-variable undefined-function
  12. type-error package-error print-not-readable stream-error end-of-file
  13. file-error storage-condition warning simple-condition simple-error
  14. simple-type-error simple-warning
  15. ;; macros:
  16. define-condition handler-bind ignore-errors handler-case
  17. with-condition-restarts restart-bind restart-case with-restarts
  18. with-simple-restart check-type assert etypecase ctypecase ecase ccase
  19. ;; functions:
  20. make-condition arithmetic-error-operation arithmetic-error-operands
  21. cell-error-name type-error-datum type-error-expected-type
  22. package-error-package print-not-readable-object stream-error-stream
  23. file-error-pathname simple-condition-format-string
  24. simple-condition-format-arguments
  25. signal restart-name compute-restarts find-restart invoke-restart
  26. invoke-restart-interactively invoke-debugger break error cerror warn
  27. ;; functions and restart names:
  28. abort continue muffle-warning store-value use-value
  29. ;; variables:
  30. *break-on-signals* *debugger-hook*
  31. ;; extensions:
  32. muffle-cerrors appease-cerrors exit-on-error
  33. ))
  34. (in-package "SYSTEM")
  35.  
  36.  
  37. ;;; Overview of Concepts
  38.  
  39. ; A condition is some information about an exceptional situation the program
  40. ; cannot or does not want handle locally.
  41. ; A handler is some code that tries to do recovery from exceptional situations
  42. ; that happen elsewhere, or that decides to transfer control.
  43. ; A restart is a point where control may be transferred to, together with a
  44. ; description what is about to happen in this case.
  45.  
  46.  
  47. ;;; The CONDITION type
  48.  
  49. ; The condition type system is integrated with CLOS.
  50. (clos:defclass condition () ())
  51.  
  52. ; 29.3.18. Printing Conditions when *print-escape* and *print-readably* are NIL.
  53. (clos:defgeneric print-condition (condition stream)
  54.   (:method ((condition condition) stream)
  55.     (format stream (ENGLISH (formatter "Condition of type ~S.")
  56.                     DEUTSCH (formatter "Ausnahmefall vom Typ ~S.")
  57.                     FRANCAIS (formatter "Condition exceptionnelle de type ~S."))
  58.                    (type-of condition)
  59.   ) )
  60. )
  61. (clos:defmethod clos:print-object ((object condition) stream)
  62.   (if (or *print-escape* *print-readably*)
  63.     (clos:call-next-method)
  64.     (print-condition object stream)
  65. ) )
  66.  
  67. ;;; 29.4.5. Defining Conditions
  68.  
  69. ; DEFINE-CONDITION, CLtL2 p. 898
  70. (defmacro define-condition (name parent-types slot-specs &rest options)
  71.   (unless (symbolp name)
  72.     (error-of-type 'program-error
  73.       (DEUTSCH "~S: Der Name einer Condition mu▀ ein Symbol sein, nicht: ~S"
  74.        ENGLISH "~S: the name of a condition must be a symbol, not ~S"
  75.        FRANCAIS "~S : Le nom d'une condition exceptionnelle doit Ωtre un symbole et non ~S")
  76.       'define-condition name
  77.   ) )
  78.   (unless (and (listp parent-types) (every #'symbolp parent-types))
  79.     (error-of-type 'program-error
  80.       (DEUTSCH "~S: Die Liste der Obertypen mu▀ eine Liste von Symbolen sein, nicht: ~S"
  81.        ENGLISH "~S: the parent-type list must be a list of symbols, not ~S"
  82.        FRANCAIS "~S : La liste des types doit Ωtre une liste de symboles et non ~S")
  83.       'define-condition parent-types
  84.   ) )
  85.   (unless (listp slot-specs)
  86.     (error-of-type 'program-error
  87.       (DEUTSCH "~S: Die Liste der Slot-Beschreibungen mu▀ eine Liste sein, nicht: ~S"
  88.        ENGLISH "~S: the slot description list must be a list, not ~S"
  89.        FRANCAIS "~S : La liste des descriptions de ½slots╗ doit Ωtre une listeet non ~S")
  90.       'define-condition slot-specs
  91.   ) )
  92.   (let ((docstring-option nil)
  93.         (report-function nil))
  94.     (dolist (option options)
  95.       (if (listp option)
  96.         (if (and (keywordp (car option)) (eql (length option) 2))
  97.           (case (first option)
  98.             (:DOCUMENTATION (setq docstring-option option))
  99.             (:REPORT (setq report-function (rest option)))
  100.             (T (error-of-type 'program-error
  101.                  (DEUTSCH "~S ~S: Die Option ~S gibt es nicht."
  102.                   ENGLISH "~S ~S: unknown option ~S"
  103.                   FRANCAIS "~S ~S : Option ~S non reconnue.")
  104.                  'define-condition name (first option)
  105.           ) )  )
  106.           (error-of-type 'program-error
  107.             (DEUTSCH "~S ~S: Falsche Syntax in ~S-Option: ~S"
  108.              ENGLISH "~S ~S: invalid syntax in ~S option: ~S"
  109.              FRANCAIS "~S ~S : Mauvaise syntaxe dans l'option ~S: ~S")
  110.             'define-condition name option
  111.         ) )
  112.         (error-of-type 'program-error
  113.           (DEUTSCH "~S ~S: Das ist keine ~S-Option: ~S"
  114.            ENGLISH "~S ~S: not a ~S option: ~S"
  115.            FRANCAIS "~S ~S : Ceci n'est pas une option ~S: ~S")
  116.           'define-condition name option
  117.     ) ) )
  118.     (let ((defclass-form
  119.             `(CLOS:DEFCLASS ,name
  120.                ,(clos::add-default-superclass parent-types 'CONDITION)
  121.                ,slot-specs
  122.                ,@(if docstring-option `(,docstring-option))
  123.              )
  124.          ))
  125.       (if report-function
  126.         `(PROGN
  127.            ,defclass-form
  128.            (CLOS:DEFMETHOD PRINT-CONDITION ((CONDITION ,name) STREAM)
  129.              ,(if (stringp (first report-function))
  130.                 `(WRITE-STRING ,(first report-function) STREAM)
  131.                 `(FUNCALL (FUNCTION ,@report-function) CONDITION STREAM)
  132.               )
  133.          ) )
  134.         defclass-form
  135. ) ) ) )
  136.  
  137. ;;; 29.4.6. Creating Conditions
  138.  
  139. ; MAKE-CONDITION, CLtL2 p. 901
  140. (defun make-condition (type &rest slot-initializations)
  141.   (unless (subtypep type 'condition)
  142.     (error-of-type 'error
  143.       (DEUTSCH "~S: Typ ~S ist kein Untertyp von ~S."
  144.        ENGLISH "~S: type ~S is not a subtype of ~S"
  145.        FRANCAIS "~S : Le type ~S n'est pas un sous-type de ~S.")
  146.       'make-condition type 'condition
  147.   ) )
  148.   (apply #'clos:make-instance type slot-initializations)
  149. )
  150.  
  151. ; canonicalize a condition argument, CLtL2 p. 888
  152. (defun coerce-to-condition (datum arguments
  153.                             caller-name
  154.                             default-type &rest more-initargs)
  155.   (typecase datum
  156.     (condition
  157.       (when arguments
  158.         (error-of-type 'type-error
  159.           :datum arguments :expected-type 'null
  160.           (DEUTSCH "~S ~S: ▄berflⁿssige Argumente ~S"
  161.            ENGLISH "~S ~S: superfluous arguments ~S"
  162.            FRANCAIS "~S ~S : Les arguments ~S sont superflus.")
  163.           caller-name datum arguments
  164.       ) )
  165.       datum
  166.     )
  167.     (symbol
  168.       (apply #'make-condition datum arguments)
  169.     )
  170.     ((or string function) ; only this case uses default-type and more-initargs
  171.       (apply #'make-condition default-type
  172.              #-dpANS :format-string #+dpANS :format-control datum
  173.              :format-arguments arguments
  174.              more-initargs
  175.     ) )
  176.     (t
  177.       (error-of-type 'type-error
  178.         :datum datum :expected-type '(or condition symbol string function)
  179.         (DEUTSCH "~S: Condition-Argument mu▀ ein String, ein Symbol oder eine Condition sein, nicht ~S"
  180.          ENGLISH "~S: the condition argument must be a string, a symbol or a condition, not ~S"
  181.          FRANCAIS "~S : L'argument de condition exceptionnelle doit Ωtre de type STRING, SYMBOL ou CONDITION et non ~S")
  182.         caller-name datum
  183. ) ) ) )
  184.  
  185. ;;; 29.5. Predefined Condition Types
  186.  
  187. ; Hierarchy:
  188. ;
  189. ;   condition
  190. ;   |
  191. ;   |-- simple-condition
  192. ;   |
  193. ;   |-- serious-condition
  194. ;   |   |
  195. ;   |   |-- error
  196. ;   |   |   |
  197. ;   |   |   |-- simple-error
  198. ;   |   |   |
  199. ;   |   |   |-- arithmetic-error
  200. ;   |   |   |   |
  201. ;   |   |   |   |-- division-by-zero
  202. ;   |   |   |   |
  203. ;   |   |   |   |-- floating-point-overflow
  204. ;   |   |   |   |
  205. ;   |   |   |   |-- floating-point-underflow
  206. ;   |   |   |
  207. ;   |   |   |-- cell-error
  208. ;   |   |   |   |
  209. ;   |   |   |   |-- unbound-variable
  210. ;   |   |   |   |
  211. ;   |   |   |   |-- undefined-function
  212. ;   |   |   |
  213. ;   |   |   |-- control-error
  214. ;   |   |   |
  215. ;   |   |   |-- file-error
  216. ;   |   |   |
  217. ;   |   |   |-- package-error
  218. ;   |   |   |
  219. ;   |   |   |-- print-not-readable
  220. ;   |   |   |
  221. ;   |   |   |-- program-error
  222. ;   |   |   |
  223. ;   |   |   |-- stream-error
  224. ;   |   |   |   |
  225. ;   |   |   |   |-- end-of-file
  226. ;   |   |   |
  227. ;   |   |   |-- type-error
  228. ;   |   |       |
  229. ;   |   |       |-- simple-type-error
  230. ;   |   |
  231. ;   |   |-- storage-condition
  232. ;   |
  233. ;   |-- warning
  234. ;       |
  235. ;       |-- simple-warning
  236. ;
  237.  
  238. ; conditions that require interactive intervention
  239. (define-condition serious-condition () ())
  240.  
  241.   ; serious conditions that occur deterministically
  242.   (define-condition error (serious-condition) ())
  243.  
  244.     ; statically detectable errors of a program
  245.     (define-condition program-error (error) ())
  246.     ; all the other errors must be detected by the runtime system
  247.  
  248.     ; not statically detectable errors in program control
  249.     (define-condition control-error (error) ())
  250.  
  251.     ; errors that occur while doing arithmetic operations
  252.     (define-condition arithmetic-error (error)
  253.       ((operation :initarg :operation :reader arithmetic-error-operation)
  254.        (operands  :initarg :operands  :reader arithmetic-error-operands)
  255.     ) )
  256.  
  257.       ; trying to evaluate a mathematical function at a singularity
  258.       (define-condition division-by-zero (arithmetic-error) ())
  259.  
  260.       ; trying to get too close to infinity in the floating point domain
  261.       (define-condition floating-point-overflow (arithmetic-error) ())
  262.  
  263.       ; trying to get too close to zero in the floating point domain
  264.       (define-condition floating-point-underflow (arithmetic-error) ())
  265.  
  266.       #+dpANS (define-condition floating-point-inexact (arithmetic-error) ())
  267.  
  268.       #+dpANS (define-condition floating-point-invalid-operation (arithmetic-error) ())
  269.  
  270.     ; trying to access a location which contains #<UNBOUND>
  271.     (define-condition cell-error (error)
  272.       ((name :initarg :name :reader cell-error-name))
  273.     )
  274.  
  275.       ; trying to get the value of an unbound variable
  276.       (define-condition unbound-variable (cell-error) ())
  277.  
  278.       ; trying to get the global function definition of an undefined function
  279.       (define-condition undefined-function (cell-error) ())
  280.  
  281.       #+dpANS (define-condition unbound-slot (cell-error)
  282.                 ((instance :initarg :instance :reader unbound-slot-instance))
  283.               )
  284.  
  285.     ; when some datum does not belong to the expected type
  286.     (define-condition type-error (error)
  287.       ((datum         :initarg :datum         :reader type-error-datum)
  288.        (expected-type :initarg :expected-type :reader type-error-expected-type)
  289.     ) )
  290.  
  291.     ; errors during operation on packages
  292.     (define-condition package-error (error)
  293.       ((package :initarg :package :reader package-error-package))
  294.     )
  295.  
  296.     ; attempted violation of *PRINT-READABLY*
  297.     (define-condition print-not-readable (error)
  298.       ((object :initarg :object :reader print-not-readable-object))
  299.     )
  300.  
  301.     #+dpANS (define-condition parse-error (error) ())
  302.  
  303.     ; errors while doing stream I/O
  304.     (define-condition stream-error (error)
  305.       ((stream :initarg :stream :reader stream-error-stream))
  306.     )
  307.  
  308.       ; unexpected end of stream
  309.       (define-condition end-of-file (stream-error) ())
  310.  
  311.       #+dpANS (define-condition reader-error (parse-error stream-error) ())
  312.  
  313.     ; errors with pathnames, OS level errors with streams
  314.     (define-condition file-error (error)
  315.       ((pathname :initarg :pathname :reader file-error-pathname))
  316.     )
  317.  
  318.   ; "Virtual memory exhausted"
  319.   (define-condition storage-condition (serious-condition) ())
  320.  
  321. ; conditions for which user notification is appropriate
  322. (define-condition warning () ())
  323.  
  324. #+dpANS (define-condition style-warning (warning) ())
  325.  
  326. ;; These shouldn't be separate types but we cannot adjoin slots without
  327. ;; defining subtypes.
  328.  
  329. ; conditions usually created by SIGNAL
  330. (define-condition simple-condition ()
  331.   (#-dpANS (format-string :initarg :format-string :initform nil
  332.                           :reader simple-condition-format-string
  333.            )
  334.    #+dpANS (format-control :initarg :format-control :initform nil
  335.                            :reader simple-condition-format-string
  336.                            :reader simple-condition-format-control
  337.            )
  338.    (format-arguments :initarg :format-arguments :initform nil
  339.                      :reader simple-condition-format-arguments
  340.   ))
  341.   #|
  342.   (:report
  343.     (lambda (condition stream)
  344.       (let ((fstring (simple-condition-format-string condition)))
  345.         (when fstring
  346.           (apply #'format stream fstring (simple-condition-format-arguments condition))
  347.   ) ) ) )
  348.   |#
  349. )
  350. ; We don't use the :report option here. Instead we define a print-condition
  351. ; method which will be executed regardless of the condition type's CPL.
  352. (clos:defmethod print-condition :around ((condition simple-condition) stream)
  353.   (let ((fstring (simple-condition-format-string condition)))
  354.     (if fstring
  355.       (apply #'format stream fstring (simple-condition-format-arguments condition))
  356.       (clos:call-next-method)
  357. ) ) )
  358.  
  359. ; conditions usually created by ERROR or CERROR
  360. (define-condition simple-error (simple-condition error) ())
  361.  
  362. ; conditions usually created by CHECK-TYPE
  363. (define-condition simple-type-error (simple-error type-error) ())
  364.  
  365. ; conditions usually created by WARN
  366. (define-condition simple-warning (simple-condition warning) ())
  367.  
  368. ; All conditions created by the C runtime code are of type simple-condition.
  369. ; Need the following types. Don't use them for discrimination.
  370. (define-condition simple-serious-condition (simple-condition serious-condition) ())
  371. (define-condition simple-program-error (simple-error program-error) ())
  372. (define-condition simple-control-error (simple-error control-error) ())
  373. (define-condition simple-arithmetic-error (simple-error arithmetic-error) ())
  374. (define-condition simple-division-by-zero (simple-error division-by-zero) ())
  375. (define-condition simple-floating-point-overflow (simple-error floating-point-overflow) ())
  376. (define-condition simple-floating-point-underflow (simple-error floating-point-underflow) ())
  377. (define-condition simple-cell-error (simple-error cell-error) ())
  378. (define-condition simple-unbound-variable (simple-error unbound-variable) ())
  379. (define-condition simple-undefined-function (simple-error undefined-function) ())
  380. (define-condition simple-package-error (simple-error package-error) ())
  381. (define-condition simple-print-not-readable (simple-error print-not-readable) ())
  382. (define-condition simple-stream-error (simple-error stream-error) ())
  383. (define-condition simple-end-of-file (simple-error end-of-file) ())
  384. (define-condition simple-file-error (simple-error file-error) ())
  385. (define-condition simple-storage-condition (simple-condition storage-condition) ())
  386.  
  387. ; Bootstrapping
  388. (%defclcs
  389.   ; The order of the types in this vector must be the same as in lispbibl.d.
  390.   '#((condition                . simple-condition)
  391.      (serious-condition        . simple-serious-condition)
  392.      (error                    . simple-error)
  393.      (program-error            . simple-program-error)
  394.      (control-error            . simple-control-error)
  395.      (arithmetic-error         . simple-arithmetic-error)
  396.      (division-by-zero         . simple-division-by-zero)
  397.      (floating-point-overflow  . simple-floating-point-overflow)
  398.      (floating-point-underflow . simple-floating-point-underflow)
  399.      (cell-error               . simple-cell-error)
  400.      (unbound-variable         . simple-unbound-variable)
  401.      (undefined-function       . simple-undefined-function)
  402.      (type-error               . simple-type-error)
  403.      (package-error            . simple-package-error)
  404.      (print-not-readable       . simple-print-not-readable)
  405.      (stream-error             . simple-stream-error)
  406.      (end-of-file              . simple-end-of-file)
  407.      (file-error               . simple-file-error)
  408.      (storage-condition        . simple-storage-condition)
  409.      (warning                  . simple-warning)
  410.     )
  411. )
  412.  
  413.  
  414. ;;; Handling and Signalling - Primitives
  415.  
  416. (defvar *break-on-signals* nil)
  417.  
  418. #|
  419. ; This would be a possible implementation. However, it forces too many
  420. ; variables into closures although in the most frequent case - no condition
  421. ; at all - they won't be needed. Furthermore, it conses too much.
  422.  
  423. ; List of active invocations of HANDLER-BIND.
  424. (defvar *handler-clusters* '())
  425.  
  426. ;; HANDLER-BIND, CLtL2 p. 898
  427. (defmacro handler-bind (clauses &body body)
  428.   `(LET ((*HANDLER-CLUSTERS*
  429.            (CONS
  430.              (LIST ,@(mapcar #'(lambda (clause)
  431.                                  (let ((type (first clause))
  432.                                        (function-form (second clause)))
  433.                                    `(CONS ',type ,function-form)
  434.                                ) )
  435.                              clauses
  436.                      )
  437.              )
  438.              *HANDLER-CLUSTERS*
  439.         )) )
  440.      (PROGN ,@body)
  441.    )
  442. )
  443.  
  444. ;; SIGNAL, CLtL2 p. 888
  445. (defun signal (datum &rest arguments)
  446.   (let ((condition
  447.           (coerce-to-condition datum arguments 'signal
  448.                                'simple-condition ; CLtL2 p. 918 specifies this
  449.        )) )
  450.     (when (typep condition *break-on-signals*)
  451.       ; Enter the debugger prior to signalling the condition
  452.       (restart-case (invoke-debugger condition)
  453.         (continue ())
  454.     ) )
  455.     ; CLtL2 p. 884: "A handler is executed in the dynamic context of the
  456.     ; signaler, except that the set of available condition handlers will
  457.     ; have been rebound to the value that was active at the time the condition
  458.     ; handler was made active."
  459.     (let ((*handler-clusters* *handler-clusters*))
  460.       (loop
  461.         (when (null *handler-clusters*) (return))
  462.         (dolist (handler (pop *handler-clusters*))
  463.           (when (typep condition (car handler))
  464.             (funcall (cdr handler) condition)
  465.             (return)
  466.     ) ) ) )
  467.     nil
  468. ) )
  469.  
  470. |#
  471.  
  472. ;; HANDLER-BIND, CLtL2 p. 898
  473. ; Since we can build handler frames only in compiled code
  474. ; there is SYS::%HANDLER-BIND which is synonymous to HANDLER-BIND except
  475. ; that SYS::%HANDLER-BIND only occurs in compiled code.
  476. (defmacro handler-bind (clauses &body body)
  477.   (let ((typespecs (mapcar #'first clauses))
  478.         (handlers (append (mapcar #'rest clauses) (list body))))
  479.     (let ((handler-vars
  480.             (mapcar #'(lambda (x) (declare (ignore x)) (gensym)) handlers)
  481.          ))
  482.       `(LET ,(mapcar #'list
  483.                handler-vars
  484.                (mapcar #'(lambda (handler) `(FUNCTION (LAMBDA () (PROGN ,@handler))))
  485.                        handlers
  486.              ) )
  487.          (LOCALLY (DECLARE (COMPILE))
  488.            (SYS::%HANDLER-BIND
  489.              ,(mapcar #'(lambda (typespec handler-var)
  490.                           `(,typespec #'(LAMBDA (CONDITION) (FUNCALL (FUNCALL ,handler-var) CONDITION)))
  491.                         )
  492.                       typespecs handler-vars
  493.               )
  494.              (FUNCALL ,(car (last handler-vars)))
  495.        ) ) )
  496. ) ) )
  497.  
  498. ;; SIGNAL, CLtL2 p. 888
  499. ; is in error.d
  500.  
  501.  
  502. ;;; Handling and Signalling - Part 2
  503.  
  504. ;; IGNORE-ERRORS, CLtL2 p. 897
  505. (defmacro ignore-errors (&body body)
  506.   (let ((blockname (gensym)))
  507.     `(BLOCK ,blockname
  508.        (HANDLER-BIND
  509.          ((ERROR #'(LAMBDA (CONDITION) (RETURN-FROM ,blockname (VALUES NIL CONDITION)))))
  510.          ,@body
  511.      ) )
  512. ) )
  513.  
  514. ;; HANDLER-CASE, CLtL2 p. 895
  515. (defmacro handler-case (form &rest clauses)
  516.   ; split off the :NO-ERROR clause and
  517.   ; add a GO tag to the other clauses (type varlist . body)
  518.   (let ((no-error-clause nil) ; the last clause, if it is a :no-error clause
  519.         (extended-clauses '())) ; ((tag type varlist . body) ...)
  520.     (do ()
  521.         ((endp clauses))
  522.       (let ((clause (pop clauses)))
  523.         (block check-clause
  524.           (unless (and (consp clause) (consp (cdr clause)) (listp (second clause)))
  525.             (error-of-type 'program-error
  526.               (DEUTSCH "~S: Illegale Syntax fⁿr Klausel: ~S"
  527.                ENGLISH "~S: illegal syntax of clause ~S"
  528.                FRANCAIS "~S : syntaxe inadmissible de la phrase ~S")
  529.               'handler-case clause
  530.           ) )
  531.           (when (eq (first clause) ':no-error)
  532.             (if (null clauses) ; at the end of the clauses?
  533.               (progn (setq no-error-clause clause) (return-from check-clause))
  534.               (warn (DEUTSCH "~S: ~S-Klausel an falscher Stelle: ~S"
  535.                      ENGLISH "~S: misplaced ~S clause: ~S"
  536.                      FRANCAIS "~S : phrase ~S mal placΘe: ~S")
  537.                     'handler-case ':no-error clause
  538.           ) ) )
  539.           (let ((varlist (second clause))) ; known as a list
  540.             (unless (null (cdr varlist))
  541.               (error-of-type 'program-error
  542.                 (DEUTSCH "~S: Zu viele Variablen ~S in Klausel ~S"
  543.                  ENGLISH "~S: too many variables ~S in clause ~S"
  544.                  FRANCAIS "~S : trop de variables ~S dans la phrase ~S")
  545.                 'handler-case varlist clause
  546.           ) ) )
  547.           (push (cons (gensym) clause) extended-clauses)
  548.     ) ) )
  549.     (setq extended-clauses (nreverse extended-clauses))
  550.     (let ((blockname (gensym))
  551.           (tempvar (gensym)))
  552.       `(BLOCK ,blockname
  553.          (LET (,tempvar) ; tempvar is IGNORABLE since it is a gensym
  554.            (TAGBODY
  555.              (RETURN-FROM ,blockname
  556.                ,(let ((main-form
  557.                         `(HANDLER-BIND
  558.                            ,(mapcar #'(lambda (xclause)
  559.                                         (let ((tag (first xclause))
  560.                                               (type (first (rest xclause)))
  561.                                               (varlist (second (rest xclause))))
  562.                                           `(,type
  563.                                             #'(LAMBDA (CONDITION)
  564.                                                 ,(if (null varlist)
  565.                                                    `(DECLARE (IGNORE CONDITION))
  566.                                                    `(SETQ ,tempvar CONDITION)
  567.                                                  )
  568.                                                 (GO ,tag)
  569.                                            )  )
  570.                                       ) )
  571.                                     extended-clauses
  572.                             )
  573.                            ,form
  574.                          )
  575.                      ))
  576.                   (if no-error-clause
  577.                     `(MULTIPLE-VALUE-CALL #'(LAMBDA ,@(rest no-error-clause))
  578.                        ,main-form
  579.                      )
  580.                     main-form
  581.                 ) )
  582.              )
  583.              ,@(mapcap #'(lambda (xclause)
  584.                            (let ((tag (first xclause))
  585.                                  (varlist (second (rest xclause)))
  586.                                  (body (cddr (rest xclause)))) ; may contain declarations
  587.                              `(,tag
  588.                                (RETURN-FROM ,blockname
  589.                                  (LET ,(if (null varlist) '() `((,@varlist ,tempvar)))
  590.                                    ,@body
  591.                               )) )
  592.                          ) )
  593.                        extended-clauses
  594.                )
  595.        ) ) )
  596. ) ) )
  597.  
  598.  
  599. ;;; Restarts
  600.  
  601. ;; This stuff is needed only once an exception has already occurred. No need
  602. ;; to optimize the hell out of it.
  603.  
  604. ; The default test function for restarts always returns T. See CLtL2 p. 905,909.
  605. (defun default-restart-test (condition)
  606.   (declare (ignore condition))
  607.   t
  608. )
  609.  
  610. ; The default interactive function for restarts returns the empty argument list.
  611. (defun default-restart-interactive ()
  612.   '()
  613. )
  614.  
  615. ;; The RESTART type, CLtL2 p. 916
  616. ;; Also defines RESTART-NAME, CLtL2 p. 911
  617. (defstruct (restart (:print-function print-restart))
  618.   name             ; its name, or NIL if it is not named
  619.   (test #'default-restart-test) ; function that tests whether this restart
  620.                                 ; applies to a given condition
  621.   (invoke-tag nil) ; tag used to invoke the restart, or nil
  622.   invoke-function  ; function used to invoke the restart, if invoke-tag is nil
  623.   (report nil)     ; function used to print a description of the restart
  624.   (interactive #'default-restart-interactive)
  625.                    ; function used to gather additional data from the user
  626.                    ; before invoking the restart
  627. )
  628. #| ; We could also define it as a CLOS class:
  629. (clos:defclass restart ()
  630.   (name            :initarg :name            :reader restart-name)
  631.   (test            :initarg :test            :reader restart-test
  632.                    :initform #'default-restart-test
  633.   )
  634.   (invoke-tag      :initarg :invoke-tag      :reader restart-invoke-tag
  635.                    :initform nil
  636.   )
  637.   (invoke-function :initarg :invoke-function :reader restart-invoke-function)
  638.   (report          :initarg :report          :reader restart-report
  639.                    :initform nil
  640.   )
  641.   (interactive     :initarg :interactive     :reader restart-interactive
  642.                    :initform #'default-restart-interactive
  643.   )
  644. )
  645. |#
  646.  
  647. ;; Printing restarts
  648. (defun print-restart (restart stream depth)
  649.   (declare (ignore depth))
  650.   (if (or *print-escape* *print-readably*)
  651.     (print-unreadable-object (restart stream :type t :identity t)
  652.       (write (restart-name restart) :stream stream)
  653.     )
  654.     (let ((report-function (restart-report restart)))
  655.       (if report-function
  656.         (funcall report-function stream)
  657.         (prin1 (restart-name restart) stream)
  658. ) ) ) )
  659. #| ; If RESTART were a CLOS class:
  660. (clos:defmethod clos:print-object ((restart restart) stream)
  661.   (if (or *print-escape* *print-readably*)
  662.     (clos:call-next-method)
  663.     (let ((report-function (restart-report restart)))
  664.       (if report-function
  665.         (funcall report-function stream)
  666.         (prin1 (restart-name restart) stream)
  667. ) ) ) )
  668. |#
  669.  
  670. ;; Expands to the equivalent of `(MAKE-RESTART :NAME name ...)
  671. ;; but makes intelligent use of the defaults to reduce code size.
  672. (defun make-restart-form (name test invoke-tag invoke-function report interactive)
  673.   `(MAKE-RESTART
  674.      :NAME ,name
  675.      ,@(if (not (equal test '(FUNCTION DEFAULT-RESTART-TEST)))
  676.          `(:TEST ,test)
  677.        )
  678.      ,@(if (not (equal invoke-tag 'NIL))
  679.          `(:INVOKE-TAG ,invoke-tag)
  680.        )
  681.      :INVOKE-FUNCTION ,invoke-function
  682.      ,@(if (not (equal report 'NIL))
  683.          `(:REPORT ,report)
  684.        )
  685.      ,@(if (not (equal interactive '(FUNCTION DEFAULT-RESTART-INTERACTIVE)))
  686.          `(:INTERACTIVE ,interactive)
  687.        )
  688.    )
  689. )
  690.  
  691. ;; The list of active restarts.
  692. (defvar *active-restarts* nil)
  693.  
  694. ;; A list of pairs of conditions and restarts associated with them. We have to
  695. ;; keep the associations separate because there can be a many-to-many mapping
  696. ;; between restarts and conditions, and this mapping has dynamic extent.
  697. (defvar *condition-restarts* nil)
  698.  
  699. ; Add an association between a condition and a couple of restarts.
  700. (defun add-condition-restarts (condition restarts)
  701.   (dolist (restart restarts)
  702.     (push (cons condition restart) *condition-restarts*)
  703. ) )
  704.  
  705. ;; WITH-CONDITION-RESTARTS, CLtL2 p. 910
  706. (defmacro with-condition-restarts (condition-form restarts-form &body body)
  707.   `(LET ((*CONDITION-RESTARTS* *CONDITION-RESTARTS*))
  708.      (ADD-CONDITION-RESTARTS ,condition-form ,restarts-form)
  709.      (LET () ,@body)
  710.    )
  711. )
  712.  
  713. ;;; 29.4.8. Finding and Manipulating Restarts
  714.  
  715. ; Tests whether a given restart is applicable to a given condition
  716. (defun applicable-restart-p (restart condition)
  717.   (and
  718.     #| ; We choose the dpANS behaviour because it makes the need for the
  719.        ; syntax-dependent implicit restart association in RESTART-CASE
  720.        ; nearly obsolete.
  721.     #-dpANS
  722.     ; A restart is applicable iff it is associated to that condition.
  723.     (dolist (asso *condition-restarts* nil)
  724.       (when (and (eq (car asso) condition) (eq (cdr asso) restart))
  725.         (return t)
  726.     ) )
  727.     #+dpANS
  728.     |#
  729.     ; A restart is applicable if it is associated to that condition
  730.     ; or if it is not associated to any condition.
  731.     (let ((not-at-all t))
  732.       (dolist (asso *condition-restarts* not-at-all)
  733.         (when (eq (cdr asso) restart)
  734.           (if (eq (car asso) condition)
  735.             (return t)
  736.             (setq not-at-all nil)
  737.     ) ) ) )
  738.     ; Call the restart's test function:
  739.     (funcall (restart-test restart) condition)
  740. ) )
  741.  
  742. ;; COMPUTE-RESTARTS, CLtL2 p. 910
  743. (defun compute-restarts (&optional condition)
  744.   (if condition
  745.     ; return only restarts that are applicable to that condition
  746.     (remove-if-not #'(lambda (restart) (applicable-restart-p restart condition))
  747.                    *active-restarts*
  748.     )
  749.     ; return all restarts
  750.     *active-restarts*
  751. ) )
  752.  
  753. ;; FIND-RESTART, CLtL2 p. 911
  754. ; returns a restart or nil
  755. (defun find-restart (restart-identifier &optional condition)
  756.   (cond ((null restart-identifier)
  757.          (error-of-type 'error
  758.            (DEUTSCH "~S: ~S ist als Restart-Name hier nicht zulΣssig. Verwenden Sie ~S."
  759.             ENGLISH "~S: ~S is not a valid restart name here. Use ~S instead."
  760.             FRANCAIS "~S : ~S n'est pas valable comme nom de ½restart╗ ici. Utilisez ~S.")
  761.            'find-restart restart-identifier 'compute-restarts
  762.         ))
  763.         ((symbolp restart-identifier)
  764.          (dolist (restart *active-restarts*)
  765.            (when (and (eq (restart-name restart) restart-identifier)
  766.                       (or (null condition)
  767.                           (applicable-restart-p restart condition)
  768.                  )    )
  769.              (return restart)
  770.         )) )
  771.         ((typep restart-identifier 'restart)
  772.          (dolist (restart *active-restarts*)
  773.            (when (and (eq restart restart-identifier)
  774.                       (or (null condition)
  775.                           (applicable-restart-p restart condition)
  776.                  )    )
  777.              (return restart)
  778.         )) )
  779.         (t (error-of-type 'type-error
  780.              :datum restart-identifier :expected-type '(or symbol restart)
  781.              (DEUTSCH "~S: Ungⁿltiger Restart-Name: ~S"
  782.               ENGLISH "~S: invalid restart name ~S"
  783.               FRANCAIS "~S : Nom inadmissible pour un ½restart╗: ~S")
  784.              'find-restart restart-identifier
  785.         )  )
  786. ) )
  787.  
  788. (defun restart-not-found (restart-identifier)
  789.   (error-of-type 'control-error
  790.     (DEUTSCH "~S: Ein Restart mit Namen ~S ist nicht sichtbar."
  791.      ENGLISH "~S: No restart named ~S is visible."
  792.      FRANCAIS "~S : Un ½restart╗ de nom ~S n'est pas visible.")
  793.     'invoke-restart restart-identifier
  794. ) )
  795.  
  796. (defun %invoke-restart (restart arguments)
  797.   (if (restart-invoke-tag restart)
  798.     (throw (restart-invoke-tag restart) arguments)
  799.     (apply (restart-invoke-function restart) arguments)
  800.     ; This may return normally, the restart need not transfer control.
  801.     ; (See CLtL2 p. 880.)
  802. ) )
  803.  
  804. ;; INVOKE-RESTART, CLtL2 p. 911
  805. (defun invoke-restart (restart-identifier &rest arguments)
  806.   (let ((restart (find-restart restart-identifier)))
  807.     (unless restart (restart-not-found restart-identifier))
  808.     (%invoke-restart restart arguments)
  809. ) )
  810.  
  811. (defun invoke-restart-condition (restart-identifier condition &rest arguments)
  812.   (let ((restart (find-restart restart-identifier condition)))
  813.     (unless restart (restart-not-found restart-identifier))
  814.     (%invoke-restart restart arguments)
  815. ) )
  816.  
  817. (defun invoke-restart-condition-if-exists (restart-identifier condition &rest arguments)
  818.   (let ((restart (find-restart restart-identifier condition)))
  819.     (when restart
  820.       (%invoke-restart restart arguments)
  821. ) ) )
  822.  
  823. ;; INVOKE-RESTART-INTERACTIVELY, CLtL2 p. 911
  824. (defun invoke-restart-interactively (restart-identifier)
  825.   (let ((restart (find-restart restart-identifier)))
  826.     (unless restart (restart-not-found restart-identifier))
  827.     (let ((arguments (funcall (restart-interactive restart))))
  828.       (%invoke-restart restart arguments)
  829. ) ) )
  830.  
  831. ;;; 29.4.7. Establishing Restarts
  832.  
  833. ;; This conses out the wazoo, but there seems to be no good way around it short
  834. ;; of special casing things a zillion ways.  The main problem is that someone
  835. ;; could write:
  836. ;;
  837. ;; (restart-bind ((nil *fun-1*
  838. ;;                     :interactive-function *fun-2*
  839. ;;                     :report-function *fun-3*
  840. ;;                     :test-function *fun-4*
  841. ;;                 )) ...)
  842. ;;
  843. ;; and it is supposed to work.
  844.  
  845. ;; RESTART-BIND, CLtL2 p. 909
  846. (defmacro restart-bind (restart-specs &body body)
  847.   (setq body `(PROGN ,@body))
  848.   (unless (listp restart-specs)
  849.     (error-of-type 'program-error
  850.       (DEUTSCH "~S: Das ist keine Liste: ~S"
  851.        ENGLISH "~S: not a list: ~S"
  852.        FRANCAIS "~S : ceci n'est pas une liste: ~S")
  853.       'restart-bind restart-specs
  854.   ) )
  855.   (if restart-specs
  856.     `(LET ((*ACTIVE-RESTARTS*
  857.              (LIST*
  858.                ,@(mapcar #'(lambda (spec)
  859.                              (unless (and (listp spec) (consp (cdr spec)) (symbolp (first spec)))
  860.                                (error-of-type 'program-error
  861.                                  (DEUTSCH "~S: Ungⁿltige Restart-Spezifikation: ~S"
  862.                                   ENGLISH "~S: invalid restart specification ~S"
  863.                                   FRANCAIS "~S : spΘcification inadmissible d'un ½restart╗: ~S")
  864.                                  'restart-bind spec
  865.                              ) )
  866.                              (apply #'(lambda (name function
  867.                                                &key (test-function '(FUNCTION DEFAULT-RESTART-TEST))
  868.                                                     (interactive-function '(FUNCTION DEFAULT-RESTART-INTERACTIVE))
  869.                                                     (report-function 'NIL))
  870.                                         (when (and (null name) (eq report-function 'NIL))
  871.                                           ; CLtL2 p. 906: "It is an error if an unnamed restart is used
  872.                                           ; and no report information is provided."
  873.                                           (error-of-type 'program-error
  874.                                             (DEUTSCH "~S: Bei unbenannten Restarts mu▀ ~S angegeben werden: ~S"
  875.                                              ENGLISH "~S: unnamed restarts require ~S to be specified: ~S"
  876.                                              FRANCAIS "~S : Il faut spΘcifier ~S pour des ½restarts╗ anonymes: ~S")
  877.                                             'restart-bind ':REPORT-FUNCTION spec
  878.                                         ) )
  879.                                         (make-restart-form `',name
  880.                                                            test-function
  881.                                                            'NIL
  882.                                                            function
  883.                                                            report-function
  884.                                                            interactive-function
  885.                                       ) )
  886.                                     spec
  887.                            ) )
  888.                          restart-specs
  889.                  )
  890.                *ACTIVE-RESTARTS*
  891.           )) )
  892.        ,body
  893.      )
  894.     body
  895. ) )
  896.  
  897. ;; RESTART-CASE, CLtL2 p. 903
  898. ;; WITH-RESTARTS
  899. ;; Syntax: (RESTART-CASE form {restart-clause}*)
  900. ;;         (WITH-RESTARTS ({restart-clause}*) {form}*)
  901. ;; restart-clause ::=   (restart-name arglist {keyword value}* {form}*)
  902. ;;                    | (restart-name {keyword value}* arglist {form}*)
  903.  
  904. ;; There are a number of special cases we could optimize for. If we
  905. ;; can determine that we will not have to cons any closures at
  906. ;; runtime, then we could statically allocate the list of restarts.
  907. ;;
  908. ;; Since we have to deal with the wacky way RESTART-CASE interacts with
  909. ;; WITH-CONDITION-RESTARTS, we do not go through RESTART-BIND.
  910.  
  911. (eval-when (compile load eval)
  912.   (defun expand-restart-case (caller restart-clauses form)
  913.     (unless (listp restart-clauses)
  914.       (error-of-type 'program-error
  915.         (DEUTSCH "~S: Das ist keine Liste: ~S"
  916.          ENGLISH "~S: not a list: ~S"
  917.          FRANCAIS "~S : ceci n'est pas une liste: ~S")
  918.         caller restart-clauses
  919.     ) )
  920.     (let ((xclauses ; list of expanded clauses
  921.                     ; ((tag name test interactive report lambdalist . body) ...)
  922.             (mapcar
  923.               #'(lambda (restart-clause &aux (clause restart-clause))
  924.                   (unless (and (consp clause) (consp (cdr clause)) (symbolp (first clause)))
  925.                     (error-of-type 'program-error
  926.                       (DEUTSCH "~S: Ungⁿltige Restart-Spezifikation: ~S"
  927.                        ENGLISH "~S: invalid restart specification ~S"
  928.                        FRANCAIS "~S : spΘcification inadmissible d'un ½restart╗: ~S")
  929.                       caller clause
  930.                   ) )
  931.                   (let ((name (pop clause))
  932.                         (passed-arglist nil)
  933.                         (passed-keywords nil)
  934.                         arglist
  935.                         (keywords '()))
  936.                     (loop
  937.                       (when (endp clause) (return))
  938.                       (cond ((and (not passed-arglist) (listp (first clause)))
  939.                              (setq arglist (pop clause))
  940.                              (setq passed-arglist t)
  941.                              (when keywords (setq passed-keywords t))
  942.                             )
  943.                             ((and (not passed-keywords) (consp (cdr clause)) (keywordp (first clause)))
  944.                              (push (pop clause) keywords)
  945.                              (push (pop clause) keywords)
  946.                             )
  947.                             (t (return))
  948.                     ) )
  949.                     (unless passed-arglist
  950.                       (error-of-type 'program-error
  951.                         (DEUTSCH "~S: Restart-Spezifikation ohne Lambda-Liste: ~S"
  952.                          ENGLISH "~S: missing lambda list in restart specification ~S"
  953.                          FRANCAIS "~S : il faut une liste lambda dans la spΘcification d'un ½restart╗: ~S")
  954.                         caller clause
  955.                     ) )
  956.                     (multiple-value-bind (test interactive report)
  957.                         (apply #'(lambda (&key (test 'DEFAULT-RESTART-TEST)
  958.                                                (interactive 'DEFAULT-RESTART-INTERACTIVE)
  959.                                                (report 'DEFAULT-RESTART-REPORT))
  960.                                    (values test interactive report)
  961.                                  )
  962.                                (nreverse keywords)
  963.                         )
  964.                       (when (and (null name) (eq report 'DEFAULT-RESTART-REPORT))
  965.                         ; CLtL2 p. 906: "It is an error if an unnamed restart is used
  966.                         ; and no report information is provided."
  967.                         (error-of-type 'program-error
  968.                           (DEUTSCH "~S: Bei unbenannten Restarts mu▀ ~S angegeben werden: ~S"
  969.                            ENGLISH "~S: unnamed restarts require ~S to be specified: ~S"
  970.                            FRANCAIS "~S : Il faut spΘcifier ~S pour des ½restarts╗ anonymes: ~S")
  971.                           caller ':REPORT restart-clause
  972.                       ) )
  973.                       (when (and (consp arglist) (not (member (first arglist) lambda-list-keywords))
  974.                                  (eq interactive 'DEFAULT-RESTART-INTERACTIVE)
  975.                             )
  976.                         ; restart takes required arguments but does not have an
  977.                         ; interactive function that will prompt for them.
  978.                         (warn (DEUTSCH "~S: Restart kann nicht interaktiv aufgerufen werden, weil ~S fehlt: ~S"
  979.                                ENGLISH "~S: restart cannot be invoked interactively because it is missing a ~S option: ~S"
  980.                                FRANCAIS "~S : Ce ½restart╗ ne peut prendre le contr⌠le en dialogue car il manque un ~S : ~S")
  981.                               caller ':INTERACTIVE restart-clause
  982.                       ) )
  983.                       `(,(gensym)
  984.                         ,name
  985.                         ,test ,interactive ,report
  986.                         ,arglist
  987.                         ,@clause
  988.                        )
  989.                 ) ) )
  990.               restart-clauses
  991.           ) )
  992.           (blockname (gensym))
  993.           (arglistvar (gensym))
  994.           (associate
  995.             ;; Yick.  As a pretty lame way of allowing for an
  996.             ;; association between conditions and restarts,
  997.             ;; RESTART-CASE has to notice if its body is signalling a
  998.             ;; condition, and, if so, associate the restarts with the
  999.             ;; condition.
  1000.             (and (consp form)
  1001.                  (case (first form) ((SIGNAL ERROR CERROR WARN) t) (t nil))
  1002.                  (gensym)
  1003.          )) )
  1004.       `(BLOCK ,blockname
  1005.          (LET (,arglistvar) ; arglistvar is IGNORABLE since it is a gensym
  1006.            (TAGBODY
  1007.              ,(let ((restart-forms
  1008.                       (mapcar #'(lambda (xclause)
  1009.                                   (let ((tag (first xclause))
  1010.                                         (name (second xclause))
  1011.                                         (test (third xclause))
  1012.                                         (interactive (fourth xclause))
  1013.                                         (report (fifth xclause)))
  1014.                                     (make-restart-form `',name
  1015.                                                        `(FUNCTION ,test)
  1016.                                                        'NIL
  1017.                                                        `(FUNCTION
  1018.                                                           (LAMBDA (&REST ARGUMENTS)
  1019.                                                             (SETQ ,arglistvar ARGUMENTS)
  1020.                                                             (GO ,tag)
  1021.                                                         ) )
  1022.                                                        (if (eq report 'DEFAULT-RESTART-REPORT)
  1023.                                                          `NIL
  1024.                                                          `(FUNCTION
  1025.                                                             ,(if (stringp report)
  1026.                                                                `(LAMBDA (STREAM) (WRITE-STRING ,report STREAM))
  1027.                                                                 report
  1028.                                                              )
  1029.                                                           )
  1030.                                                        )
  1031.                                                        `(FUNCTION ,interactive)
  1032.                                      )
  1033.                                 ) )
  1034.                               xclauses
  1035.                     ) )
  1036.                     (form `(RETURN-FROM ,blockname ,form)))
  1037.                 `(LET* ,(if associate
  1038.                           `((,associate (LIST ,@restart-forms))
  1039.                             (*ACTIVE-RESTARTS* (APPEND ,associate *ACTIVE-RESTARTS*))
  1040.                             (*CONDITION-RESTARTS* *CONDITION-RESTARTS*)
  1041.                            )
  1042.                           `((*ACTIVE-RESTARTS* (LIST* ,@restart-forms *ACTIVE-RESTARTS*)))
  1043.                         )
  1044.                    ,(if associate
  1045.                       #| ; This code resignals the condition in a different dynamic context!
  1046.                       `(LET ((CONDITION
  1047.                                (HANDLER-CASE ,form ; evaluate the form
  1048.                                  (CONDITION (C) C) ; catch the condition
  1049.                             )) )
  1050.                          (WITH-CONDITION-RESTARTS CONDITION ,associate ; associate the condition with the restarts
  1051.                            (SIGNAL CONDITION) ; resignal the condition
  1052.                        ) )
  1053.                       |#
  1054.                       #| ; This code invokes the debugger even if it shouldn't!
  1055.                       `(HANDLER-BIND
  1056.                          ((CONDITION ; catch the condition
  1057.                             #'(LAMBDA (CONDITION)
  1058.                                 (WITH-CONDITION-RESTARTS CONDITION ,associate  ; associate the condition with the restarts
  1059.                                   (SIGNAL CONDITION) ; resignal the condition
  1060.                                   (INVOKE-DEBUGGER CONDITION) ; this is weird!
  1061.                          ))   ) )
  1062.                          ,form
  1063.                        )
  1064.                       |#
  1065.                       `(HANDLER-BIND
  1066.                          ((CONDITION ; catch the condition
  1067.                             #'(LAMBDA (CONDITION)
  1068.                                 (ADD-CONDITION-RESTARTS CONDITION ,associate) ; associate the condition with the restarts
  1069.                                 (SIGNAL CONDITION) ; resignal the condition
  1070.                          ))   )
  1071.                          ,form
  1072.                        )
  1073.                       form
  1074.                     )
  1075.                  )
  1076.               )
  1077.              ,@(mapcap #'(lambda (xclause)
  1078.                            (let ((tag (first xclause))
  1079.                                  (lambdabody (cdddr (cddr xclause))))
  1080.                              `(,tag
  1081.                                (RETURN-FROM ,blockname
  1082.                                  (APPLY (FUNCTION (LAMBDA ,@lambdabody)) ,arglistvar)
  1083.                               ))
  1084.                          ) )
  1085.                        xclauses
  1086.                )
  1087.        ) ) )
  1088.   ) )
  1089. )
  1090.  
  1091. (defmacro restart-case (form &rest restart-clauses)
  1092.   (expand-restart-case 'restart-case restart-clauses form)
  1093. )
  1094.  
  1095. (defmacro with-restarts (restart-clauses &body body)
  1096.   (expand-restart-case 'with-restarts restart-clauses `(PROGN ,@body))
  1097. )
  1098.  
  1099. ;; WITH-SIMPLE-RESTART, CLtL2 p. 902
  1100. (defmacro with-simple-restart ((name format-string &rest format-arguments) &body body)
  1101.   (if (or format-arguments (not (constantp format-string)))
  1102.     `(WITH-RESTARTS
  1103.          ((,name
  1104.            :REPORT (LAMBDA (STREAM) (FORMAT STREAM ,format-string ,@format-arguments))
  1105.            () (VALUES NIL T)
  1106.          ))
  1107.        ,@body
  1108.      )
  1109.     ;; Here's an example of how we can easily optimize things. There is no
  1110.     ;; need to refer to anything in the lexical environment, so we can avoid
  1111.     ;; consing a restart at run time.
  1112.     (let ((blockname (gensym))
  1113.           (tag (gensym)))
  1114.       `(BLOCK ,blockname
  1115.          (CATCH ',tag
  1116.            (LET ((*ACTIVE-RESTARTS*
  1117.                    (CONS
  1118.                      (LOAD-TIME-VALUE
  1119.                        (MAKE-RESTART :NAME ',name
  1120.                                      :INVOKE-TAG ',tag
  1121.                                      :REPORT #'(LAMBDA (STREAM) (FORMAT STREAM ,format-string))
  1122.                      ) )
  1123.                      *ACTIVE-RESTARTS*
  1124.                 )) )
  1125.              (RETURN-FROM ,blockname (PROGN ,@body))
  1126.            )
  1127.            (VALUES NIL T)
  1128.        ) )
  1129. ) ) )
  1130.  
  1131.  
  1132. ;;; 29.4.10. Restart Functions
  1133.  
  1134. ;; These functions are customary way to pass control from a handler to a
  1135. ;; restart. They just invoke the restart of the same name.
  1136.  
  1137. ;; ABORT, CLtL2 p. 913
  1138. (defun abort (&optional condition)
  1139.   (invoke-restart-condition 'abort condition)
  1140. )
  1141.  
  1142. ;; CONTINUE, CLtL2 p. 913
  1143. (defun continue (&optional condition)
  1144.   (invoke-restart-condition-if-exists 'continue condition)
  1145. )
  1146.  
  1147. ;; MUFFLE-WARNING, CLtL2 p. 913
  1148. (defun muffle-warning (&optional condition)
  1149.   (invoke-restart-condition 'muffle-warning condition)
  1150. )
  1151.  
  1152. ;; STORE-VALUE, CLtL2 p. 913
  1153. (defun store-value (value &optional condition)
  1154.   (invoke-restart-condition-if-exists 'store-value condition value)
  1155. )
  1156.  
  1157. ;; USE-VALUE, CLtL2 p. 914
  1158. (defun use-value (value &optional condition)
  1159.   (invoke-restart-condition-if-exists 'use-value condition value)
  1160. )
  1161.  
  1162.  
  1163. ;;; 29.4.2. Assertions
  1164.  
  1165. ;; These macros supersede the corresponding ones from macros2.lsp.
  1166.  
  1167. (defun report-new-value (stream)
  1168.   (write-string (DEUTSCH "Sie dⁿrfen einen neuen Wert eingeben."
  1169.                  ENGLISH "You may input a new value."
  1170.                  FRANCAIS "Vous avez l'occasion d'entrer une nouvelle valeur.")
  1171.                 stream
  1172. ) )
  1173.  
  1174. (defun prompt-for-new-value (place)
  1175.   (format *query-io*
  1176.           (DEUTSCH "~%Neues ~S: "
  1177.            ENGLISH "~%New ~S: "
  1178.            FRANCAIS "~%Nouveau ~S : ")
  1179.           place
  1180.   )
  1181.   (read *query-io*)
  1182. )
  1183.  
  1184. ;; CHECK-TYPE, CLtL2 p. 889
  1185. (defmacro check-type (place typespec &optional (string nil))
  1186.   (let ((tag1 (gensym))
  1187.         (tag2 (gensym))
  1188.         (var (gensym)))
  1189.     `(TAGBODY
  1190.        ,tag1
  1191.        (LET ((,var ,place))
  1192.          (WHEN (TYPEP ,var ',typespec) (GO ,tag2))
  1193.          (RESTART-CASE
  1194.            (ERROR-OF-TYPE 'TYPE-ERROR
  1195.              :DATUM ,var :EXPECTED-TYPE ',typespec
  1196.              (DEUTSCH "~A~%Der Wert ist: ~S"
  1197.               ENGLISH "~A~%The value is: ~S"
  1198.               FRANCAIS "~A~%La valeur est : ~S")
  1199.              (DEUTSCH ,(format nil "Der Wert von ~S sollte ~:[vom Typ ~S~;~:*~A~] sein."
  1200.                                    place string typespec
  1201.                        )
  1202.               ENGLISH ,(format nil "The value of ~S should be ~:[of type ~S~;~:*~A~]."
  1203.                                    place string typespec
  1204.                        )
  1205.               FRANCAIS ,(format nil "La valeur de ~S devrait Ωtre ~:[de type ~S~;~:*~A~]."
  1206.                                     place string typespec
  1207.                         )
  1208.              )
  1209.              ,var
  1210.            )
  1211.            ; only one restart, will "continue" invoke it?
  1212.            (STORE-VALUE
  1213.              :REPORT REPORT-NEW-VALUE
  1214.              :INTERACTIVE (LAMBDA () (LIST (PROMPT-FOR-NEW-VALUE ',place)))
  1215.              (NEW-VALUE) (SETF ,place NEW-VALUE)
  1216.            )
  1217.        ) )
  1218.        (GO ,tag1)
  1219.        ,tag2
  1220.      )
  1221. ) )
  1222.  
  1223. (defun report-no-new-value (stream)
  1224.   (write-string (DEUTSCH "Neuer Anlauf"
  1225.                  ENGLISH "Retry"
  1226.                  FRANCAIS "ReΘssayer")
  1227.                 stream
  1228. ) )
  1229.  
  1230. (defun report-new-values (stream)
  1231.   (write-string (DEUTSCH "Sie dⁿrfen neue Werte eingeben."
  1232.                  ENGLISH "You may input new values."
  1233.                  FRANCAIS "Vous pouvez entrer de nouvelles valeurs.")
  1234.                 stream
  1235. ) )
  1236.  
  1237. ;; ASSERT, CLtL2 p. 891
  1238. (defmacro assert (test-form &optional (place-list nil) (datum nil) &rest args)
  1239.   (let ((tag1 (gensym))
  1240.         (tag2 (gensym)))
  1241.     `(TAGBODY
  1242.        ,tag1
  1243.        (WHEN ,test-form (GO ,tag2))
  1244.        (RESTART-CASE
  1245.          (PROGN ; no need for explicit association, see applicable-restart-p
  1246.            (ERROR ; of-type ??
  1247.              ,@(if datum
  1248.                  `(,datum ,@args) ; use coerce-to-condition??
  1249.                  `("~A"
  1250.                    (DEUTSCH ,(format nil "Der Wert von ~S darf nicht NIL sein." test-form)
  1251.                     ENGLISH ,(format nil "~S must evaluate to a non-NIL value." test-form)
  1252.                     FRANCAIS ,(format nil "La valeur de ~S ne peut pas Ωtre NIL." test-form)
  1253.                   ))
  1254.                )
  1255.          ) )
  1256.          ; only one restart: CONTINUE
  1257.          (CONTINUE
  1258.            :REPORT ,(case (length place-list)
  1259.                       (0 'REPORT-NO-NEW-VALUE)
  1260.                       (1 'REPORT-NEW-VALUE)
  1261.                       (t 'REPORT-NEW-VALUES)
  1262.                     )
  1263.            :INTERACTIVE
  1264.              (LAMBDA ()
  1265.                (LIST
  1266.                  ,@(mapcar #'(lambda (place) `(PROMPT-FOR-NEW-VALUE ',place))
  1267.                            place-list
  1268.                    )
  1269.              ) )
  1270.            ,@(let ((new-value-vars
  1271.                      (mapcar #'(lambda (place) (declare (ignore place)) (gensym))
  1272.                              place-list
  1273.                   )) )
  1274.                `(,new-value-vars
  1275.                  ,@(mapcar #'(lambda (place var) `(SETF ,place ,var))
  1276.                            place-list new-value-vars
  1277.                 )  )
  1278.              )
  1279.        ) )
  1280.        (GO ,tag1)
  1281.        ,tag2
  1282.      )
  1283. ) )
  1284.  
  1285. ;;; 29.4.3. Exhaustive Case Analysis
  1286.  
  1287. ;; These macros supersede the corresponding ones from macros2.lsp.
  1288.  
  1289. (flet ((typecase-errorstring (keyform keyclauselist)
  1290.          (let ((typelist (mapcar #'first keyclauselist)))
  1291.            `(DEUTSCH ,(format nil "Der Wert von ~S mu▀ einem der Typen ~{~S~^, ~} angeh÷ren." keyform typelist)
  1292.              ENGLISH ,(format nil "The value of ~S must be of one of the types ~{~S~^, ~}" keyform typelist)
  1293.              FRANCAIS ,(format nil "La valeur de ~S doit appartenir α l'un des types ~{~S~^, ~}." keyform typelist)
  1294.             )
  1295.        ) )
  1296.        (typecase-expected-type (keyclauselist)
  1297.          `(OR ,@(mapcar #'first keyclauselist))
  1298.        )
  1299.        (case-errorstring (keyform keyclauselist)
  1300.          (let ((caselist
  1301.                  (mapcap #'(lambda (keyclause)
  1302.                              (setq keyclause (car keyclause))
  1303.                              (if (listp keyclause) keyclause (list keyclause))
  1304.                            )
  1305.                          keyclauselist
  1306.               )) )
  1307.            `(DEUTSCH ,(format nil "Der Wert von ~S mu▀ einer der folgenden sein: ~{~S~^, ~}" keyform caselist)
  1308.              ENGLISH ,(format nil "The value of ~S must be one of ~{~S~^, ~}" keyform caselist)
  1309.              FRANCAIS ,(format nil "La valeur de ~S doit Ωtre l'une des suivantes : ~{~S~^, ~}" keyform caselist)
  1310.             )
  1311.        ) )
  1312.        (case-expected-type (keyclauselist)
  1313.          `(MEMBER ,@(mapcap #'(lambda (keyclause)
  1314.                                 (setq keyclause (car keyclause))
  1315.                                 (if (listp keyclause) keyclause (list keyclause))
  1316.                               )
  1317.                             keyclauselist
  1318.           )         )
  1319.        )
  1320.        (simply-error (casename form clauselist errorstring expected-type)
  1321.          (let ((var (gensym)))
  1322.            `(LET ((,var ,form))
  1323.               (,casename ,var
  1324.                 ,@clauselist ; if a clause contains an OTHERWISE or T key,
  1325.                              ; we could treat it specially or warn about it.
  1326.                 (OTHERWISE
  1327.                   (ERROR-OF-TYPE 'TYPE-ERROR
  1328.                     :DATUM ,var :EXPECTED-TYPE ',expected-type
  1329.                     (DEUTSCH "~A~%Der Wert ist: ~S"
  1330.                      ENGLISH "~A~%The value is: ~S"
  1331.                      FRANCAIS "~A~%La valeur est : ~S")
  1332.                     ,errorstring ,var
  1333.             ) ) ) )
  1334.        ) )
  1335.        (retry-loop (casename place clauselist errorstring)
  1336.          (let ((g (gensym))
  1337.                (h (gensym)))
  1338.            `(BLOCK ,g
  1339.               (TAGBODY
  1340.                 ,h
  1341.                 (RETURN-FROM ,g
  1342.                   (,casename ,place
  1343.                     ,@clauselist ; if a clause contains an OTHERWISE or T key,
  1344.                                  ; we could treat it specially or warn about it.
  1345.                     (OTHERWISE
  1346.                       (RESTART-CASE
  1347.                         (PROGN ; no need for explicit association, see applicable-restart-p
  1348.                           (ERROR ; of-type ??
  1349.                             (DEUTSCH "~A~%Der Wert ist: ~S"
  1350.                              ENGLISH "~A~%The value is: ~S"
  1351.                              FRANCAIS "~A~%La valeur est : ~S")
  1352.                             ,errorstring
  1353.                             ,place
  1354.                         ) )
  1355.                         ; only one restart, will "continue" invoke it?
  1356.                         (STORE-VALUE
  1357.                           :REPORT REPORT-NEW-VALUE
  1358.                           :INTERACTIVE (LAMBDA () (LIST (PROMPT-FOR-NEW-VALUE ',place)))
  1359.                           (NEW-VALUE) (SETF ,place NEW-VALUE)
  1360.                       ) )
  1361.                       (GO ,h)
  1362.             ) ) ) ) )
  1363.       )) )
  1364.   (defmacro etypecase (keyform &rest keyclauselist)
  1365.     (simply-error 'TYPECASE keyform keyclauselist
  1366.                   (typecase-errorstring keyform keyclauselist)
  1367.                   (typecase-expected-type keyclauselist)
  1368.   ) )
  1369.   (defmacro ctypecase (keyplace &rest keyclauselist)
  1370.     (retry-loop 'TYPECASE keyplace keyclauselist
  1371.                 (typecase-errorstring keyplace keyclauselist)
  1372.   ) )
  1373.   (defmacro ecase (keyform &rest keyclauselist)
  1374.     (simply-error 'CASE keyform keyclauselist
  1375.                   (case-errorstring keyform keyclauselist)
  1376.                   (case-expected-type keyclauselist)
  1377.   ) )
  1378.   (defmacro ccase (keyform &rest keyclauselist)
  1379.     (retry-loop 'CASE keyform keyclauselist
  1380.                 (case-errorstring keyform keyclauselist)
  1381.   ) )
  1382. )
  1383.  
  1384. ;;; 29.4.11. Debugging Utilities
  1385.  
  1386. (defvar *debugger-hook* nil)
  1387.  
  1388. ;; INVOKE-DEBUGGER, CLtL2 p. 915
  1389. ; is in error.d
  1390.  
  1391. ;; BREAK, CLtL2 p. 914
  1392. ; (BREAK [format-string {arg}*])
  1393. ; It would be unfair to bypass the *debugger-hook* test in INVOKE-DEBUGGER.
  1394. ; So we call INVOKE-DEBUGGER and therefore need a condition.
  1395. (defun break (&optional (format-string "Break") &rest args)
  1396.   (let ((condition
  1397.           (make-condition 'simple-condition
  1398.                           :format-string format-string
  1399.                           :format-arguments args
  1400.        )) )
  1401.     (with-restarts
  1402.         ((CONTINUE
  1403.           :report (lambda (stream)
  1404.                     (format stream (DEUTSCH "~S-Schleife beenden."
  1405.                                     ENGLISH "Return from ~S loop"
  1406.                                     FRANCAIS "Quitter le cycle de ~S.")
  1407.                                    'break
  1408.                   ) )
  1409.           ()
  1410.         ))
  1411.       (with-condition-restarts condition (list (find-restart 'CONTINUE))
  1412.         (invoke-debugger condition)
  1413.   ) ) )
  1414.   nil
  1415. )
  1416.  
  1417. ;;; 29.4.1. Signaling Conditions
  1418.  
  1419. ;; ERROR, CLtL2 p. 886
  1420. #| ; is in error.d
  1421. (defun error (errorstring &rest args)
  1422.   (if (or *error-handler* (not *use-clcs*))
  1423.     (progn
  1424.       (if *error-handler*
  1425.         (apply *error-handler* nil errorstring args)
  1426.         (progn
  1427.           (terpri *error-output*)
  1428.           (write-string "*** - " *error-output*)
  1429.           (apply #'format *error-output* errorstring args)
  1430.       ) )
  1431.       (funcall *break-driver* nil)
  1432.     )
  1433.     (let ((condition (coerce-to-condition errorstring args 'error 'simple-error)))
  1434.       (signal condition)
  1435.       (invoke-debugger condition)
  1436.     )
  1437. ) )
  1438. |#
  1439.  
  1440. ;; CERROR, CLtL2 p. 887
  1441. (defun cerror (continue-format-string error-format-string &rest args)
  1442.   (if *error-handler*
  1443.     (apply *error-handler*
  1444.            (or continue-format-string t) error-format-string args
  1445.     )
  1446.     (if (not *use-clcs*)
  1447.       (progn
  1448.         (terpri *error-output*)
  1449.         (write-string "** - Continuable Error" *error-output*)
  1450.         (terpri *error-output*)
  1451.         (apply #'format *error-output* error-format-string args)
  1452.         (terpri *debug-io*)
  1453.         (if (interactive-stream-p *debug-io*)
  1454.           (progn
  1455.             (write-string (DEUTSCH "Wenn Sie (mit Continue) fortfahren: "
  1456.                            ENGLISH "If you continue (by typing 'continue'): "
  1457.                            FRANCAIS "Si vous continuez (en tapant 'continue'): ")
  1458.                           *debug-io*
  1459.             )
  1460.             (apply #'format *debug-io* continue-format-string args)
  1461.             (funcall *break-driver* t)
  1462.           )
  1463.           (apply #'format *debug-io* continue-format-string args)
  1464.       ) )
  1465.       (let ((condition (coerce-to-condition error-format-string args 'cerror 'simple-error)))
  1466.         (with-restarts
  1467.             ((CONTINUE
  1468.               :report (lambda (stream)
  1469.                         (apply #'format stream continue-format-string args)
  1470.                       )
  1471.               ()
  1472.             ))
  1473.           (with-condition-restarts condition (list (find-restart 'CONTINUE))
  1474.             (signal condition)
  1475.             (invoke-debugger condition)
  1476.       ) ) )
  1477.   ) )
  1478.   nil
  1479. )
  1480.  
  1481. ;;; 29.4.9. Warnings
  1482.  
  1483. ;; WARN, CLtL2 p. 912
  1484. ; (WARN format-string {arg}*)
  1485. (defun warn (format-string &rest args)
  1486.   (if (not *use-clcs*)
  1487.     (progn
  1488.       (terpri *error-output*)
  1489.       (write-string (DEUTSCH "WARNUNG:"
  1490.                      ENGLISH "WARNING:"
  1491.                      FRANCAIS "AVERTISSEMENT :")
  1492.                     *error-output*
  1493.       )
  1494.       (terpri *error-output*)
  1495.       (apply #'format *error-output* format-string args)
  1496.       (when *break-on-warnings* (funcall *break-driver* t))
  1497.     )
  1498.     (block warn
  1499.       (let ((condition (coerce-to-condition format-string args 'warn 'simple-warning)))
  1500.         (unless (typep condition 'warning)
  1501.           (error-of-type 'type-error
  1502.             :datum condition :expected-type 'warning
  1503.             (DEUTSCH "~S: Das ist ernster als eine Warnung: ~A"
  1504.              ENGLISH "~S: This is more serious than a warning: ~A"
  1505.              FRANCAIS "~S : C'est plus sΘrieux qu'un avertissement: ~A")
  1506.             'warn condition
  1507.         ) )
  1508.         (with-restarts
  1509.             ((MUFFLE-WARNING
  1510.                () (return-from warn)
  1511.             ))
  1512.           (with-condition-restarts condition (list (find-restart 'MUFFLE-WARNING))
  1513.             (signal condition)
  1514.         ) )
  1515.         (terpri *error-output*)
  1516.         (write-string (DEUTSCH "WARNUNG:"
  1517.                        ENGLISH "WARNING:"
  1518.                        FRANCAIS "AVERTISSEMENT :")
  1519.                       *error-output*
  1520.         )
  1521.         (terpri *error-output*)
  1522.         (print-condition condition *error-output*)
  1523.         (when *break-on-warnings*
  1524.           (with-restarts
  1525.               ((CONTINUE
  1526.                 :report (lambda (stream)
  1527.                           (format stream (DEUTSCH "~S-Schleife beenden."
  1528.                                           ENGLISH "Return from ~S loop"
  1529.                                           FRANCAIS "Quitter le cycle de ~S.")
  1530.                                          'break
  1531.                         ) )
  1532.                 () (return-from warn)
  1533.               ))
  1534.             (with-condition-restarts condition (list (find-restart 'CONTINUE))
  1535.               ; We don't call  (invoke-debugger condition)  because that
  1536.               ; would tell the user about a "Continuable error". Actually,
  1537.               ; it is only a warning!
  1538.               (funcall *break-driver* nil condition nil)
  1539.         ) ) )
  1540.     ) )
  1541.   )
  1542.   nil
  1543. )
  1544.  
  1545.  
  1546. ;; Bootstrapping done. Activate the Condition System.
  1547. (setq *use-clcs* t)
  1548.  
  1549.  
  1550. #|
  1551. Todo:
  1552. 29.3.6 29.3.7 29.3.8 29.3.9 29.3.10
  1553.       29.3.11 29.3.12 29.3.13 29.3.14 29.3.15 29.3.16 29.3.17 29.3.18
  1554. 29.4. 29.4.9 29.4.11
  1555. 29.5.
  1556. |#
  1557.  
  1558.  
  1559. ;; Extensions. They assume *USE-CLCS* is T.
  1560.  
  1561. ; (MUFFLE-CERRORS {form}*) executes the forms, but when a continuable
  1562. ; error occurs, the CONTINUE restart is silently invoked.
  1563. (defmacro muffle-cerrors (&body body)
  1564.   `(HANDLER-BIND ((ERROR #'(LAMBDA (CONDITION) (CONTINUE CONDITION))))
  1565.      ,@body
  1566.    )
  1567. )
  1568. #| ; This works as well, but looks more like a hack.
  1569. (defmacro muffle-cerrors (&body body)
  1570.   (let ((old-debugger-hook (gensym)))
  1571.     `(LET* ((,old-debugger-hook *DEBUGGER-HOOK*) 
  1572.             (*DEBUGGER-HOOK*
  1573.               #'(LAMBDA (CONDITION DEBUGGER-HOOK)
  1574.                   (CONTINUE CONDITION)
  1575.                   (WHEN ,old-debugger-hook
  1576.                     (FUNCALL ,old-debugger-hook CONDITION ,old-debugger-hook)
  1577.                 ) )
  1578.            ))
  1579.        (PROGN ,@body)
  1580.      )
  1581. ) )
  1582. |#
  1583.  
  1584. ; (APPEASE-CERRORS {form}*) executes the forms, but turns continuable errors
  1585. ; into warnings. A continuable error is signalled again as a warning, then
  1586. ; its CONTINUE restart is invoked.
  1587. (defmacro appease-cerrors (&body body)
  1588.   `(HANDLER-BIND ((ERROR #'(LAMBDA (CONDITION) (APPEASE-CERROR CONDITION))))
  1589.      ,@body
  1590.    )
  1591. )
  1592. (defun appease-cerror (condition)
  1593.   (let ((restart (find-restart 'CONTINUE condition)))
  1594.     (when restart
  1595.       (warn "~A" (with-output-to-string (stream)
  1596.                    (print-condition condition stream)
  1597.                    (let ((report-function (restart-report restart)))
  1598.                      (when report-function
  1599.                        (terpri stream)
  1600.                        (funcall report-function stream)
  1601.       )          ) ) )
  1602.       (invoke-restart restart)
  1603. ) ) )
  1604.  
  1605. ; (EXIT-ON-ERROR {form}*) executes the forms, but exits Lisp if a
  1606. ; non-continuable error occurs.
  1607. (defmacro exit-on-error (&body body)
  1608.   `(HANDLER-BIND ((ERROR #'(LAMBDA (CONDITION) (EXITONERROR CONDITION))))
  1609.      ,@body
  1610.    )
  1611. )
  1612. (defun exitonerror (condition)
  1613.   (unless (find-restart 'CONTINUE condition)
  1614.     (terpri *error-output*)
  1615.     (write-string "*** - " *error-output*)
  1616.     (print-condition condition *error-output*)
  1617.     (exit t) ; exit Lisp with error
  1618. ) )
  1619.  
  1620. ; (SYSTEM::BATCHMODE-ERRORS {form}*) executes the forms, but handles errors
  1621. ; just as a batch program should do: continuable errors are signalled as
  1622. ; warnings, non-continuable errors cause Lisp to exit.
  1623. (defmacro batchmode-errors (&body body)
  1624.   `(EXIT-ON-ERROR (APPEASE-CERRORS ,@body))
  1625. )
  1626.  
  1627.